/*
 * Copyright (C) 2008 Alessandro Sivieri <alessandro.sivieri@chimera-bellerofonte.eu>.
 *
 *  This file is part of Daex.
 *
 *  Daex is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Foobar is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.chimera_bellerofonte.tesi.datatypes;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.logging.Level;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.sax.SAXSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

import eu.chimera_bellerofonte.tesi.DaexLogger;


/**
 * <p>Classe per l'implementazione di salvataggio e caricamento
 * degli oggetti di tipo Album su XML.</p>
 * <p>La fase di caricamento necessita del corrispondente dvd inserito
 * nel drive, poiche' viene immediatamente effettuato un controllo
 * sulla corrispondenza del file con questo.</p>
 * <p>Viene infine eseguito anche un controllo di coerenza del file
 * XML da caricare con l'XML Schema corrispondente, cosi' da verificare
 * che sia stato passato un file creato correttamente da questo programma.
 * </p>
 * 
 * @see eu.chimera_bellerofonte.tesi.datatypes.Album
 * @author  Sivieri Alessandro
 * @author  Vitucci Nicola
 * @version  21/apr/07
 */
public class DefaultAlbumWriter implements AlbumWriter
{
	/**
	 * Tag di inizio del documento root
	 */
	public static final String DOC="daex:daex";
	/**
	 * Tag di inizio delle informazioni generali dell'album
	 */
	public static final String ALBUM_TAG="album";
	/**
	 * Nome dell'attributo che indica il titolo da cui si
	 * estraggono le canzoni
	 */
	public static final String ALBUM_ATTR_TITLE="dvd_title";
	/**
	 * Nome dell'attributo che indica il numero di canzoni
	 * contenute nell'album
	 */
	public static final String ALBUM_ATTR_SONGS="song_number";
	/**
	 * Tag per il riconoscimento unico del dvd; firma a 16 bit
	 */
	public static final String DVD_HASH="hash";
	/**
	 * Tag di inizio dell'elenco delle canzoni
	 */
	public static final String SONGS_TAG="songs";
	/**
	 * Tag di inizio delle informazioni delle canzoni
	 */
	public static final String SONG_TAG="song";
	/**
	 * Nome dell'attributo che indica il capitolo d'inizio
	 * della traccia
	 */
	public static final String SONG_START_ATTR="start";
	/**
	 * Nome dell'attributo che indica il capitolo finale
	 * della traccia
	 */
	public static final String SONG_END_ATTR="end";
	/**
	 * Nome dell'attributo che indica quale valore visualizzare
	 * sulla tabella di presentazione
	 */
	public static final String SONG_TABLE_VALUE="table";
	/**
	 * Nome dell'attributo che indica quale valore assoluto
	 * e' associato alla traccia
	 */
	public static final String SONG_ABSOLUTE_VALUE="absolute";
	/**
	 * Tag di descrizione di un titolo
	 */
	public static final String TITLE_TAG="title";
	/**
	 * Tag di descrizione dell'artista
	 */
	public static final String ARTIST_TAG="artist";
	/**
	 * Tag di descrizione del genere
	 */
	public static final String GENRE_TAG="genre";
	
	private Album album=null;
	private File filename=null;
	private String device=DvdNative.getDefaultDevice();
	
	private void validate() throws AlbumReaderException
	{
		try
		{
			SchemaFactory factory=SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
			Schema schema=factory.newSchema(DefaultAlbumWriter.class.getResource("/eu/chimera_bellerofonte/tesi/datatypes/album.xsd"));
			Validator validator=schema.newValidator();
			InputSource document=new InputSource(new FileInputStream(filename));
			validator.validate(new SAXSource(document));
		}
		catch(SAXParseException e)
		{
			throw new AlbumReaderException("Errore di validazione: "+e.getLocalizedMessage());
		}
		catch(Exception e)
		{
			throw new AlbumReaderException("Errore in costruzione della validazione", e);
		}
	}
	
	/**
	 * Costruttore.
	 * 
	 * @param filename l'oggetto File da cui caricare/su cui salvare l'oggetto
	 */
	public DefaultAlbumWriter(File filename)
	{
		this.filename=filename;
	}
	
	/**
	 * Costruttore.
	 * 
	 * @param filename il nome del file da cui caricare/su cui salvare l'oggetto
	 */
	public DefaultAlbumWriter(String filename)
	{
		this.filename=new File(filename);
	}

	/**
	 * Questo metodo ritorna il valore attuale di album.
	 * @return  il valore attuale di album
	 * @uml.property  name="album"
	 */
	public Album getAlbum()
	{
		return album;
	}

	/**
	 * Metodo per il caricamento di un album utilizzando un device
	 * differente da quello di default specificandolo; tale device viene
	 * utilizzato per il controllo del dvd fisico inserito.
	 * 
	 * @see eu.chimera_bellerofonte.tesi.datatypes.AlbumWriter#load()
	 * @param device un device specifico differente da quello di default
	 * @return l'oggetto album caricato
	 * @throws AlbumReaderException eccezione lanciata in caso di problemi
	 */
	public Album load(String device) throws AlbumReaderException
	{
		this.device=device;
		return load();
	}
	
	/**
	 * Metodo per il caricamento di un album utilizzando il device
	 * di default; tale device viene
	 * utilizzato per il controllo del dvd fisico inserito.
	 * 
	 * @see eu.chimera_bellerofonte.tesi.datatypes.AlbumWriter#load()
	 * @return l'oggetto album caricato
	 * @throws AlbumReaderException eccezione lanciata in caso di problemi
	 */
	public Album load() throws AlbumReaderException
	{
		Album album=new Album();
		Song song=null;
		FileInputStream file=null;
		XMLStreamReader doc=null;
		String temp1=null, temp2="", content=null;
		boolean album_start=false, song_start=false;
		int title_temp=0;
		
		try
		{
			validate();
			file=new FileInputStream(filename);
			doc=XMLInputFactory.newInstance().createXMLStreamReader(file);
			for(int event=doc.next(); event!=XMLStreamConstants.END_DOCUMENT; event=doc.next())
			{
				switch(event)
				{
					case XMLStreamConstants.START_ELEMENT:
						temp1=doc.getLocalName();
						temp2=temp1;
						if(ALBUM_TAG.equals( temp1 ))
						{
							album_start=true;
						}
						if(SONG_TAG.equals( temp1 ))
						{
							song_start=true;
							song=new Song();
						}
						if(album_start && title_temp==0)
						{
							title_temp=Integer.parseInt(doc.getAttributeValue(null, ALBUM_ATTR_TITLE));
						}
						if(song_start && song.getStartChapter()==0)
						{
							song.setStartChapter(Integer.parseInt(doc.getAttributeValue(null, SONG_START_ATTR)));
							song.setEndChapter(Integer.parseInt(doc.getAttributeValue(null, SONG_END_ATTR)));
							song.setTableValue(Integer.parseInt(doc.getAttributeValue(null, SONG_TABLE_VALUE)));
							song.setAbsoluteValue(Integer.parseInt(doc.getAttributeValue(null, SONG_ABSOLUTE_VALUE)));
						}
						break;
					case XMLStreamConstants.CHARACTERS:
						content=doc.getText();
						if(DVD_HASH.equals( temp2 ))
						{
							album.setDvd(new DvdNative(device, title_temp, content));
						}
						else if(ARTIST_TAG.equals( temp2 ))
						{
							if(album_start)
							{
								album.setArtist(content);
							}
							else
							{
								song.setArtist(content);
							}
						}
						else if(TITLE_TAG.equals( temp2 ))
						{
							if(album_start)
							{
								album.setAlbum(content);
							}
							else
							{
								song.setTitle(content);
							}
						}
						else if(GENRE_TAG.equals( temp2 ))
						{
							if(album_start)
							{
								album.setGenre(Enum.valueOf(Genre.class, content));
							}
							else
							{
								song.setGenre(Enum.valueOf(Genre.class, content));
							}
						}
						// non c'è else perchè non dovrebbero essere presenti altri valori
						break;
					case XMLStreamConstants.END_ELEMENT:
						if(ALBUM_TAG.equals( doc.getLocalName() ))
						{
							album_start=false;
						}
						if(SONG_TAG.equals( doc.getLocalName() ))
						{
							song_start=false;
							song.setAlbum(album.getAlbum());
							album.addSong(song);
						}
						if(album_start || song_start)
						{
							temp2="";
						}
						break;
					default: 
						break;
				}
			}
			this.album=album;
			DaexLogger.getDaexLogger().info("Album caricato con successo dal file "+filename);
			return this.album;
		}
		catch (Exception e)
		{
			DaexLogger.getDaexLogger().log(Level.WARNING, "Impossibile caricare il file da "+filename, e);
			throw new AlbumReaderException(e);
		}
		finally
		{
			try
			{
				if(doc!=null)
				{
					doc.close();
				}
				if(file!=null)
				{
					file.close();
				}
			}
			catch (Exception e)
			{
				// ignorato
			}
		}
	}

	/**
	 * @see eu.chimera_bellerofonte.tesi.datatypes.AlbumWriter#save(eu.chimera_bellerofonte.tesi.datatypes.Album)
	 */
	public void save(Album album) throws AlbumWriterException
	{
		FileOutputStream file=null;
		XMLStreamWriter doc=null;
		
		try
		{
			file=new FileOutputStream(filename);
			doc=XMLOutputFactory.newInstance().createXMLStreamWriter(file);
			doc.writeStartDocument();
			doc.writeStartElement(DOC);
			doc.writeAttribute("xmlns:daex", "http://www.chimera-bellerofonte.eu/daex");
			// informazioni generali Album
			doc.writeStartElement(ALBUM_TAG);
			doc.writeAttribute(ALBUM_ATTR_TITLE, ""+album.getDvdTitle());
			doc.writeAttribute(ALBUM_ATTR_SONGS, ""+album.getSongs().size());
			doc.writeStartElement(DVD_HASH);
			doc.writeCharacters(album.getDvd().getHash().toLowerCase());
			doc.writeEndElement();
			doc.writeStartElement(ARTIST_TAG);
			doc.writeCharacters(album.getArtist());
			doc.writeEndElement();
			doc.writeStartElement(TITLE_TAG);
			doc.writeCharacters(album.getAlbum());
			doc.writeEndElement();
			doc.writeStartElement(GENRE_TAG);
			doc.writeCharacters(album.getGenre().toString());
			doc.writeEndElement();
			doc.writeEndElement();
			// fine informazioni generali Album
			// informazioni sulle canzoni
			doc.writeStartElement(SONGS_TAG);
			for(Song s: album.getSongs())
			{
				doc.writeStartElement(SONG_TAG);
				doc.writeAttribute(SONG_START_ATTR, ""+s.getStartChapter());
				doc.writeAttribute(SONG_END_ATTR, ""+s.getEndChapter());
				doc.writeAttribute(SONG_TABLE_VALUE, ""+s.getTableValue());
				doc.writeAttribute(SONG_ABSOLUTE_VALUE, ""+s.getAbsoluteValue());
				doc.writeStartElement(ARTIST_TAG);
				doc.writeCharacters(s.getArtist());
				doc.writeEndElement();
				doc.writeStartElement(TITLE_TAG);
				doc.writeCharacters(s.getTitle());
				doc.writeEndElement();
				doc.writeStartElement(GENRE_TAG);
				doc.writeCharacters(s.getGenre().toString());
				doc.writeEndElement();
				doc.writeEndElement();
			}
			doc.writeEndElement();
			// fine informazioni sulle canzoni
			doc.writeEndElement();
			doc.writeEndDocument();
			DaexLogger.getDaexLogger().info("Album salvato con successo dal file "+filename);
		}
		catch (Exception e)
		{
			DaexLogger.getDaexLogger().log(Level.WARNING, "Impossibile salvare l'Album su file "+filename, e);
			throw new AlbumWriterException(e);
		}
		finally
		{
			try
			{
				if(doc!=null)
				{
					doc.flush();
					doc.close();
				}
				if(file!=null)
				{
					file.close();
				}
			}
			catch (Exception e)
			{
				// ignorato
			}
		}
	}

}
